Kattava opas globaaleille kehittäjille Pythonin http.serverin (ent. BaseHTTPServer) mukauttamiseen yksinkertaisten API:iden, dynaamisten verkkopalvelimien ja tehokkaiden sisäisten työkalujen rakentamiseen.
Pythonin sisäänrakennetun HTTP-palvelimen hallinta: Syvällinen sukellus mukauttamiseen
Python on tunnettu "paristot mukana" -filosofiastaan, joka tarjoaa rikkaan standardikirjaston, jonka avulla kehittäjät voivat rakentaa toiminnallisia sovelluksia minimaalisilla ulkoisilla riippuvuuksilla. Yksi hyödyllisimmistä, mutta usein huomiotta jäävistä näistä paristoista on sisäänrakennettu HTTP-palvelin. Olitpa sitten tietoinen sen modernista Python 3 -nimestä, http.server
, tai sen perintönimestä Python 2:ssa, BaseHTTPServer
, tämä moduuli on portti web-protokollien ymmärtämiseen ja kevyiden web-palvelujen rakentamiseen.
Vaikka monet kehittäjät kohtaavat sen ensin yhtenä rivinä tiedostojen palvelemiseen hakemistossa, sen todellinen voima piilee sen laajennettavuudessa. Aliluokittelemalla sen ydinkomponentit voit muuttaa tämän yksinkertaisen tiedostopalvelimen mukautetuksi web-sovellukseksi, mock-API:ksi frontend-kehitykseen, tietojen vastaanottimeksi IoT-laitteille tai tehokkaaksi sisäiseksi työkaluksi. Tämä opas vie sinut perusasioista edistyneeseen mukauttamiseen, ja antaa sinulle mahdollisuuden hyödyntää tätä upeaa moduulia omissa projekteissasi.
Perusteet: Yksinkertainen palvelin komentoriviltä
Ennen kuin sukellamme koodiin, tarkastellaan yleisintä käyttötapausta. Jos sinulla on Python asennettuna, sinulla on jo web-palvelin. Navigoi mihin tahansa hakemistoon tietokoneellasi käyttämällä päätettä tai komentokehotetta ja suorita seuraava komento (Python 3:lle):
python -m http.server 8000
Heti sinulla on web-palvelin käynnissä portissa 8000, joka palvelee nykyisen sijaintisi tiedostoja ja alihakemistoja. Voit käyttää sitä selaimestasi osoitteessa http://localhost:8000
. Tämä on uskomattoman hyödyllistä:
- Tiedostojen nopeaan jakamiseen paikallisessa verkossa.
- Yksinkertaisten HTML-, CSS- ja JavaScript-projektien testaamiseen ilman monimutkaista asennusta.
- Web-palvelimen käsittelemään erilaisia pyyntöjä.
Tämä yhdellä rivillä oleva komento on kuitenkin vasta jäävuoren huippu. Se suorittaa valmiiksi rakennetun, geneerisen palvelimen. Mukautetun logiikan lisäämiseksi, erilaisten pyyntötyyppien käsittelemiseksi tai dynaamisen sisällön luomiseksi meidän on kirjoitettava oma Python-skriptimme.
Ydinkomponenttien ymmärtäminen
Tällä moduulilla luotu web-palvelin koostuu kahdesta pääosasta: palvelimesta ja käsittelijästä. Heidän erillisten rooliensa ymmärtäminen on avain tehokkaaseen mukauttamiseen.
1. Palvelin: HTTPServer
Palvelimen tehtävä on kuunnella saapuvia verkkoyhteyksiä tietyssä osoitteessa ja portissa. Se on moottori, joka hyväksyy TCP-yhteydet ja välittää ne käsittelijälle käsiteltäväksi. http.server
-moduulissa tämän käsittelee tyypillisesti HTTPServer
-luokka. Luot siitä ilmentymän antamalla palvelimen osoitteen (tuple kuten ('localhost', 8000)
) ja käsittelijäluokan.
Sen päävastuu on verkkopistorasian hallinta ja pyyntö-vastaus-syklin orkestrointi. Useimpien mukautusten osalta sinun ei tarvitse muokata itse HTTPServer
-luokkaa, mutta on olennaista tietää, että se on olemassa ja pyörittää showta.
2. Käsittelijä: BaseHTTPRequestHandler
Tässä tapahtuu taikaa. Käsittelijä vastaa saapuvan HTTP-pyynnön jäsentämisestä, sen ymmärtämisestä, mitä asiakas pyytää, ja sopivan HTTP-vastauksen luomisesta. Aina kun palvelin saa uuden pyynnön, se luo ilmentymän käsittelijäluokastasi sen käsittelemiseksi.
http.server
-moduuli tarjoaa muutamia valmiita käsittelijöitä:
BaseHTTPRequestHandler
: Tämä on perustavanlaatuisin käsittelijä. Se jäsentelee pyynnön ja otsikot, mutta ei tiedä, miten vastata tiettyihin pyyntömenetelmiin, kuten GET tai POST. Se on täydellinen perusluokka, josta periytyä, kun haluat rakentaa kaiken alusta alkaen.SimpleHTTPRequestHandler
: Tämä periiBaseHTTPRequestHandler
-luokan ja lisää logiikan tiedostojen palvelemiseen nykyisestä hakemistosta. Kun suoritatpython -m http.server
, käytät tätä käsittelijää. Se on erinomainen lähtökohta, jos haluat lisätä mukautettua logiikkaa oletusarvoisen tiedostojen palvelemiskäyttäytymisen päälle.CGIHTTPRequestHandler
: Tämä laajentaaSimpleHTTPRequestHandler
-luokkaa ja käsittelee myös CGI-skriptejä. Tämä on harvinaisempaa modernissa web-kehityksessä, mutta on osa kirjaston historiaa.
Lähes kaikissa mukautetuissa palvelintehtävissä työhösi kuuluu uuden luokan luominen, joka perii BaseHTTPRequestHandler
tai SimpleHTTPRequestHandler
-luokan ja ohittaa sen menetelmät.
Ensimmäinen mukautettu palvelin: "Hello, World!" -esimerkki
Siirrytään komentoriviltä eteenpäin ja kirjoitetaan yksinkertainen Python-skripti palvelimelle, joka vastaa mukautetulla viestillä. Perimme BaseHTTPRequestHandler
-luokasta ja toteutamme do_GET
-menetelmän, joka kutsutaan automaattisesti kaikkien HTTP GET -pyyntöjen käsittelemiseksi.
Luo tiedosto nimeltä custom_server.py
:
# Käytä http.server Python 3:lle
from http.server import BaseHTTPRequestHandler, HTTPServer
import time
hostName = "localhost"
serverPort = 8080
class MyServer(BaseHTTPRequestHandler):
def do_GET(self):
# 1. Lähetä vastauksen tila koodi
self.send_response(200)
# 2. Lähetä otsikot
self.send_header("Content-type", "text/html")
self.end_headers()
# 3. Kirjoita vastausrunko
self.wfile.write(bytes("<html><head><title>My Custom Server</title></head>", "utf-8"))
self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
self.wfile.write(bytes("<body>", "utf-8"))
self.wfile.write(bytes("<p>This is a custom server, created with Python's http.server.</p>", "utf-8"))
self.wfile.write(bytes("</body></html>", "utf-8"))
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print(f"Server started http://{hostName}:{serverPort}")
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
Suorita tämä suorittamalla python custom_server.py
päätteessäsi. Kun vierailet osoitteessa http://localhost:8080
selaimessasi, näet mukautetun HTML-viestisi. Jos vierailet eri polulla, kuten http://localhost:8080/some/path
, viesti heijastaa tätä polkua.
Katsotaanpa do_GET
-menetelmää:
self.send_response(200)
: Tämä lähettää HTTP-tilajonon.200 OK
on standardivastaus onnistuneesta pyynnöstä.self.send_header("Content-type", "text/html")
: Tämä lähettää HTTP-otsikon. Tässä kerromme selaimelle, että lähettämämme sisältö on HTML-muotoista. Tämä on ratkaisevan tärkeää, jotta selain hahmontaa sivun oikein.self.end_headers()
: Tämä lähettää tyhjän rivin, joka merkitsee HTTP-otsikoiden loppua ja vastausrunkon alkua.self.wfile.write(...)
:self.wfile
on tiedostomainen objekti, johon voit kirjoittaa vastausrunkoasi. Se odottaa tavuja, ei merkkijonoja, joten meidän on koodattava HTML-merkkijonot tavuiksi käyttämälläbytes("...", "utf-8")
.
Edistynyt mukauttaminen: Käytännön reseptit
Nyt kun ymmärrät perusteet, tutkitaan tehokkaampia mukautuksia.
POST-pyyntöjen käsittely (do_POST
)
Web-sovellusten on usein vastaanotettava tietoja, esimerkiksi HTML-lomakkeesta tai API-kutsusta. Tämä tehdään tyypillisesti POST-pyynnöllä. Tämän käsittelemiseksi ohitat do_POST
-menetelmän.
do_POST
:n sisällä sinun on luettava pyyntörunko. Tämän rungon pituus määritetään Content-Length
-otsikossa.
Tässä on esimerkki käsittelijästä, joka lukee JSON-tietoja POST-pyynnöstä ja toistaa sen takaisin:
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
class APIServer(BaseHTTPRequestHandler):
def _send_cors_headers(self):
"""Lähettää otsikot, jotka sallivat cross-origin -pyynnöt"""
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type")
def do_OPTIONS(self):
"""Käsittelee pre-flight CORS -pyynnöt"""
self.send_response(200)
self._send_cors_headers()
self.end_headers()
def do_POST(self):
# 1. Lue content-length-otsikko
content_length = int(self.headers['Content-Length'])
# 2. Lue pyyntörunko
post_data = self.rfile.read(content_length)
# Demonstroinnin vuoksi, kirjaamme vastaanotetut tiedot
print(f"Received POST data: {post_data.decode('utf-8')}")
# 3. Käsittele tiedot (tässä me vain toistamme ne takaisin JSONina)
try:
received_json = json.loads(post_data)
response_data = {"status": "success", "received_data": received_json}
except json.JSONDecodeError:
self.send_response(400) # Bad Request
self.end_headers()
self.wfile.write(bytes('{"error": "Invalid JSON"}', "utf-8"))
return
# 4. Lähetä vastaus
self.send_response(200)
self._send_cors_headers()
self.send_header("Content-type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(response_data).encode("utf-8"))
# Pääsuorituslohko pysyy samana...
if __name__ == "__main__":
# ... (käytä samaa HTTPServer-asennusta kuin ennen, mutta APIServer-käsittelijänä)
server_address = ('localhost', 8080)
httpd = HTTPServer(server_address, APIServer)
print('Starting server on port 8080...')
httpd.serve_forever()
Huomautus CORS:sta: do_OPTIONS
-menetelmä ja _send_cors_headers
-funktio sisältyvät Cross-Origin Resource Sharing (CORS) -käsittelyyn. Tämä on usein välttämätöntä, jos kutsut API:asi web-sivulta, joka palvelee eri alkuperästä (verkkotunnus/portti).
Yksinkertaisen API:n rakentaminen JSON-vastauksilla
Laajennetaan edellistä esimerkkiä luodaksemme palvelin, jolla on perusreititys. Voimme tarkastaa self.path
-määritteen määrittääksemme, mitä resurssia asiakas pyytää, ja vastata sen mukaisesti. Tämän avulla voimme luoda useita API-päätepisteitä yhdellä palvelimella.
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
# Mock-tiedot
users = {
1: {"name": "Alice", "country": "Canada"},
2: {"name": "Bob", "country": "Australia"}
}
class APIHandler(BaseHTTPRequestHandler):
def _set_headers(self, status_code=200):
self.send_response(status_code)
self.send_header("Content-type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
def do_GET(self):
parsed_path = urlparse(self.path)
path = parsed_path.path
if path == "/api/users":
self._set_headers()
self.wfile.write(json.dumps(list(users.values())).encode("utf-8"))
elif path.startswith("/api/users/"):
try:
user_id = int(path.split('/')[-1])
user = users.get(user_id)
if user:
self._set_headers()
self.wfile.write(json.dumps(user).encode("utf-8"))
else:
self._set_headers(404)
self.wfile.write(json.dumps({"error": "User not found"}).encode("utf-8"))
except ValueError:
self._set_headers(400)
self.wfile.write(json.dumps({"error": "Invalid user ID"}).encode("utf-8"))
else:
self._set_headers(404)
self.wfile.write(json.dumps({"error": "Not Found"}).encode("utf-8"))
# Pääsuorituslohko kuten ennenkin, käyttäen APIHandler
# ...
Tämän käsittelijän avulla palvelimellasi on nyt primitiivinen reititysjärjestelmä:
- GET-pyyntö osoitteeseen
/api/users
palauttaa luettelon kaikista käyttäjistä. - GET-pyyntö osoitteeseen
/api/users/1
palauttaa Alicen tiedot. - Mikä tahansa muu polku johtaa 404 Not Found -virheeseen.
Tiedostojen ja dynaamisen sisällön palveleminen yhdessä
Mitä jos haluat dynaamisen API:n, mutta haluat myös palvella staattisia tiedostoja (kuten index.html
) samasta palvelimesta? Helpoin tapa on periä SimpleHTTPRequestHandler
-luokasta ja delegoida sen oletuskäyttäytymiseen, kun pyyntö ei vastaa mukautettuja polkujasi.
super()
-funktio on paras ystäväsi tässä. Sen avulla voit kutsua yliluokan menetelmää.
import json
from http.server import SimpleHTTPRequestHandler, HTTPServer
class HybridHandler(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/api/status':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {'status': 'ok', 'message': 'Server is running'}
self.wfile.write(json.dumps(response).encode('utf-8'))
else:
# Muille poluille, palataan oletusarvoiseen tiedostojen palvelemiskäyttäytymiseen
super().do_GET()
# Pääsuorituslohko kuten ennenkin, käyttäen HybridHandler
# ...
Jos nyt luot index.html
-tiedoston samassa hakemistossa ja suoritat tämän skriptin, sivun http://localhost:8080/
vierailu palvelee HTML-tiedostoasi, kun taas sivun http://localhost:8080/api/status
vierailu palauttaa mukautetun JSON-vastauksen.
Huomautus Python 2:sta (BaseHTTPServer
)
Vaikka Python 2:ta ei enää tueta, saatat törmätä perintökoodiin, joka käyttää sen HTTP-palvelimen versiota. Konseptit ovat identtisiä, mutta moduulin nimet ovat erilaiset. Tässä on nopea käännösopas:
- Python 3:
http.server
-> Python 2:BaseHTTPServer
,SimpleHTTPServer
- Python 3:
socketserver
-> Python 2:SocketServer
- Python 3:
from http.server import BaseHTTPRequestHandler
-> Python 2:from BaseHTTPServer import BaseHTTPRequestHandler
Menetelmien nimet (do_GET
, do_POST
) ja ydinlogiikka pysyvät samoina, mikä tekee vanhojen skriptien siirtämisestä Python 3:een suhteellisen yksinkertaista.
Tuotantoon liittyvät seikat: Milloin siirtyä eteenpäin
Pythonin sisäänrakennettu HTTP-palvelin on ilmiömäinen työkalu, mutta sillä on rajoituksia. On ratkaisevan tärkeää ymmärtää, milloin se on oikea valinta ja milloin sinun pitäisi hakea vankempi ratkaisu.
1. Samanaikaisuus ja suorituskyky
Oletuksena HTTPServer
on yksisäikeinen ja käsittelee pyynnöt peräkkäin. Jos yhden pyynnön käsittely kestää kauan, se estää kaikki muut saapuvat pyynnöt. Hieman kehittyneempiin käyttötapauksiin voit käyttää socketserver.ThreadingMixIn
-luokkaa luodaksesi monisäikeisen palvelimen:
from socketserver import ThreadingMixIn
from http.server import HTTPServer
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
"""Käsittele pyynnöt erillisessä säikeessä."""
pass
# Päälohkoosi, käytä tätä HTTPServerin sijaan:
# webServer = ThreadingHTTPServer((hostName, serverPort), MyServer)
Vaikka tämä auttaa samanaikaisuutta, sitä ei vieläkään ole suunniteltu suorituskykyiseen, suurille liikennemäärille tarkoitetuissa tuotantoympäristöissä. Täysimittaiset web-kehykset ja sovelluspalvelimet (kuten Gunicorn tai Uvicorn) on optimoitu suorituskykyä, resurssien hallintaa ja skaalautuvuutta varten.
2. Turvallisuus
http.server
ei ole rakennettu turvallisuus ensisijaisena painopisteenä. Siltä puuttuu sisäänrakennettu suojaus yleisiltä web-haavoittuvuuksilta, kuten Cross-Site Scripting (XSS), Cross-Site Request Forgery (CSRF) tai SQL-injektio. Tuotantoluokan kehykset, kuten Django, Flask ja FastAPI, tarjoavat nämä suojaukset heti käyttövalmiina.
3. Ominaisuudet ja abstraktio
Kun sovelluksesi kasvaa, haluat ominaisuuksia, kuten tietokantaintegraation (ORMs), templaattimoottorit, hienostuneen reitityksen, käyttäjän todennuksen ja väliohjelmat. Vaikka voisit rakentaa kaiken tämän itse http.server
-palvelimen päälle, keksisit olennaisesti web-kehyksen uudelleen. Kehykset, kuten Flask, Django ja FastAPI, tarjoavat nämä komponentit hyvin jäsennellyllä, taistelussa testatulla ja ylläpidettävällä tavalla.
Käytä http.server
-palvelinta:
- Oppimiseen ja HTTP:n ymmärtämiseen.
- Nopeaan prototyyppien luomiseen ja konseptitodistuksiin.
- Yksinkertaisten, vain sisäisten työkalujen tai kojelautojen rakentamiseen.
- Mock-API-palvelimien luomiseen frontend-kehitykseen.
- Kevyisiin tiedonkeruupäätteisiin IoT:lle tai skripteille.
Siirry kehykseen:
- Julkisille web-sovelluksille.
- Monimutkaisille API:ille, joilla on todennus ja tietokantayhteydet.
- Sovelluksille, joissa turvallisuus, suorituskyky ja skaalautuvuus ovat kriittisiä.
Johtopäätös: Yksinkertaisuuden ja hallinnan voima
Pythonin http.server
on osoitus kielen käytännöllisestä suunnittelusta. Se tarjoaa yksinkertaisen mutta tehokkaan perustan kaikille, jotka tarvitsevat työtä web-protokollien kanssa. Oppimalla mukauttamaan sen pyyntökäsittelijöitä saat tarkan hallinnan pyyntö-vastaus-syklistä, jonka avulla voit rakentaa laajan valikoiman hyödyllisiä työkaluja ilman täysimittaisen web-kehyksen ylikuormitusta.
Seuraavan kerran, kun tarvitset nopean web-palvelun, mock-API:n tai haluat vain kokeilla HTTP:tä, muista tämä monipuolinen moduuli. Se on enemmän kuin pelkkä tiedostopalvelin; se on tyhjä kangas web-pohjaisille luomuksillesi, sisältyy suoraan Pythonin standardikirjastoon.